package org.example.config;

import com.alibaba.fastjson.JSONObject;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.example.constant.AuthConstants;
import org.example.constant.BusinessEnum;
import org.example.constant.HttpContains;
import org.example.model.Result;
import org.example.impl.UserDetailsServiceImpl;
import org.example.model.LoginResult;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.security.authentication.AccountExpiredException;
import org.springframework.security.authentication.AccountStatusException;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.InsufficientAuthenticationException;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import org.springframework.security.web.authentication.logout.LogoutSuccessHandler;

import java.io.PrintWriter;
import java.time.Duration;

@Configuration
public class AuthSecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    private UserDetailsServiceImpl userDetailsService;

    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    @Override
    protected void configure(AuthenticationManagerBuilder  auth) throws Exception {
        auth.userDetailsService(userDetailsService);
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        //关闭跨站请求伪造
        http.csrf().disable();
        //关闭跨域请求
        http.cors().disable();
        //关闭session使用策略
        http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);

        //配置登录信息
        http.formLogin().loginProcessingUrl(AuthConstants.LOGIN_URL)
                .successHandler(authenticationSuccessHandler())
                .failureHandler(authenticationFailureHandler());
        //配置登出信息
        http.logout().logoutUrl(AuthConstants.LOGOUT_URL)
                .logoutSuccessHandler(logoutSuccessHandler());
        //所有请求都需要身份验证
        http.authorizeRequests().anyRequest().authenticated();

    }

    @Bean
    public AuthenticationSuccessHandler authenticationSuccessHandler(){
        return ( request, response, authentication) -> {
            //设置响应头信息
            response.setContentType( HttpContains.APPLICATION_JSON);
            response.setCharacterEncoding(HttpContains.UTF_8);

            //使用UUID作为token
            String token = java.util.UUID.randomUUID().toString();

            //从Security框架中获取用户对象并转为JSON字符串
            String userJsonStr = JSONObject.toJSONString( authentication.getPrincipal());

            stringRedisTemplate.opsForValue().set(AuthConstants.LOGIN_TOKEN_PREFIX + token,
                    userJsonStr ,
                    Duration.ofSeconds(AuthConstants.token_time)
            );

            LoginResult loginResult = new LoginResult(token, AuthConstants.token_time);

            Result<Object> result = Result.success(loginResult);

            ObjectMapper objectMapper = new ObjectMapper();
            String s = objectMapper.writeValueAsString(result);
            PrintWriter writer = response.getWriter();
            writer.write(s);
            writer.flush();
            writer.close();

        };
    }

    @Bean
    public AuthenticationFailureHandler authenticationFailureHandler(){
        return ( request, response, exception) -> {
            //设置响应头信息
            response.setContentType(HttpContains.APPLICATION_JSON);
            response.setCharacterEncoding(HttpContains.UTF_8);

            // 创建统一响应结果对象
            Result<Object> result = new Result<>();
            result.setCode(BusinessEnum.OPERATION_FAIL.getCode());
            if (exception instanceof BadCredentialsException) {
                result.setMessage("用户名或密码有误");
            } else if (exception instanceof UsernameNotFoundException) {
                result.setMessage("用户不存在");
            }else if(exception instanceof AccountExpiredException){
                result.setMessage("用户状态异常");
            } else if (exception instanceof AccountStatusException) {
                result.setMessage("用户状态异常");
            }else if (exception instanceof InsufficientAuthenticationException) {
                result.setMessage(exception.getMessage());
            } else {
                result.setMessage(BusinessEnum.OPERATION_FAIL.getDesc());
            }

            ObjectMapper objectMapper = new ObjectMapper();
            String s = objectMapper.writeValueAsString(result);
            PrintWriter writer = response.getWriter();
            writer.write(s);
            writer.flush();
            writer.close();

        };
    }

    @Bean
    public LogoutSuccessHandler logoutSuccessHandler(){
        return ( request, response, authentication) -> {
            //设置响应头信息
            response.setContentType(HttpContains.APPLICATION_JSON);
            response.setCharacterEncoding(HttpContains.UTF_8);
            String au = request.getHeader(AuthConstants.AUTHORIZATION);
            String token = au.replaceFirst(AuthConstants.BEARER, "");

            //删除Redis中的token
            stringRedisTemplate.delete(AuthConstants.LOGIN_TOKEN_PREFIX + token);

            Result<Object> result = Result.success(null);

            ObjectMapper objectMapper = new ObjectMapper();
            String s = objectMapper.writeValueAsString(result);
            PrintWriter writer = response.getWriter();
            writer.write(s);
            writer.flush();
            writer.close();
        };
    }

    @Bean
    public BCryptPasswordEncoder bCryptPasswordEncoder(){
        return new BCryptPasswordEncoder();
    }


}
